/*
 * Copyright © 2018 www.noark.xyz All Rights Reserved.
 *
 * 感谢您选择Noark框架，希望我们的努力能为您提供一个简单、易用、稳定的服务器端框架 ！
 * 除非符合Noark许可协议，否则不得使用该文件，您可以下载许可协议文件：
 *
 * 		http://www.noark.xyz/LICENSE
 *
 * 1.未经许可，任何公司及个人不得以任何方式或理由对本框架进行修改、使用和传播;
 * 2.禁止在本项目或任何子项目的基础上发展任何派生版本、修改版本或第三方版本;
 * 3.无论你对源代码做出任何修改和改进，版权都归Noark研发团队所有，我们保留所有权利;
 * 4.凡侵犯Noark版权等知识产权的，必依法追究其法律责任，特此郑重法律声明！
 */
package com.company.slg.login;

import xyz.noark.core.annotation.Controller;
import xyz.noark.core.annotation.PlayerId;
import xyz.noark.core.annotation.controller.ExecThreadGroup;
import xyz.noark.core.annotation.controller.PacketMapping;
import xyz.noark.core.network.Sender;
import xyz.noark.core.network.Session;
import xyz.noark.core.network.Session.State;
import xyz.noark.core.network.SessionManager;
import xyz.noark.network.SocketSession;

import static xyz.noark.log.LogHelper.logger;

/**
 * 登录协议入口类.
 *
 * @author 小流氓(176543888 @ qq.com)
 */
@Controller(threadGroup = ExecThreadGroup.ModuleThreadGroup)
public class LoginController {

    /**
     * 登录协议的处理入口.
     * <p>
     * opcode = 1001 这个就是登录协议对应的协议编号，全局唯一，一个协议对应一个协议内容<br>
     * state = State.CONNECTED 这个是Session在状态下可访问此方法，登录是第一个协议，所以这个状态就是刚刚链接的状态
     *
     * @param request 这个登录协议的内容对象
     */
    @PacketMapping(opcode = 1001, state = State.CONNECTED)
    public void login(SocketSession session, LoginRequest request) {
        logger.info("登录请求 username={}, password={}", request.getUsername(), request.getPassword());
        // 处理入口的参数, 都是可选的，比如Session对象，协议内容，玩家ID（登录绑定后才可以注入）
        // 登录接口一般会注入Session对象，方便对Session的状态或信息绑定等操作
        // SocketSession是Session的一个实现，如果是WebSocket链接，也可以注入WebSocketSession

        // 假设账号密码是这情况就算登录成功
        if ("noark".equals(request.getUsername()) && "123456".equals(request.getPassword())) {
            logger.info("登录成功 username={}", request.getUsername());

            // 修正Session的状态，给他绑定一些玩家信息
            // 绑定玩家Uid，状态切换为已登录，如果有创建角色的需求
            session.setUid(request.getUsername());
            session.setState(State.AUTHENTICATED);
            // 绑定用户的UID对应的Session，这里是有返回值的，就是原来有在线的情况，顶号
            SessionManager.setUidAndSession(request.getUsername(), session).ifPresent(this::kickout);

            // 如果是自动创建角色，绑定玩家的PlayerId，状态切换为已进入游戏
            session.setPlayerId(request.getUsername());
            session.setState(State.INGAME);
            // 绑定玩家ID对应的Session，这是框架处理对玩家发协议
            SessionManager.bindPlayerIdAndSession(session.getPlayerId(), session);


            // 也可以在这里发一些内容给客户端，让客户端继续其他系统数初始化
            session.send(200, "你进入XX地图，坐标X,Y");
        }
        // 密码不对，给个提示
        else {
            session.send(110, "错误提示协议");
        }
    }

    /**
     * 初始化背包请求
     * <p>
     * state = State.INGAME 这里没有显示写状态，默认就是你已进入游戏的状态，如果没有过登录，这个协议请求是过不了鉴权逻辑的
     */
    @PacketMapping(opcode = 2001)
    public void initBagReq(@PlayerId String playerId) {
        // 这里选择注入PlayerId, 由于在登录逻辑里Session已绑定过了，所以是可以直接注入使用
        // 根据玩家ID进行ORM载入他的背包数据，拼成背包初始化协议，直接使用Sender发送就好了
        Sender.relayPacket(playerId, 2002, "背包数据协议对象");
    }

    private void kickout(Session session) {
        // 这个UID之前绑定的Session，需要处理顶号逻辑
        // 这里一定要使用sendAndClose，发送完再关闭
        // 你不要调Send和Close, 原因自己点进去看看啦，不懂可以来请我吃饭
        session.sendAndClose(110, "这是顶号协议对象");
    }
}